﻿#include "service_context_impl.hh"
#include "box/box_channel.hh"
#include "box/service_box.hh"
#include "box_alloc.hh"
#include "console/box_console_impl.hh"
#include "console/console_impl.hh"
#include "detail/box_alloc.hh"
#include "detail/co_manager_impl.h"
#include "detail/command_impl.hh"
#include "detail/component_impl.hh"
#include "detail/coro_runner_impl.hh"
#include "detail/csv_reader_impl.hh"
#include "detail/http_base_impl.hh"
#include "detail/lang_impl.hh"
#include "detail/local_system_time_impl.hh"
#include "detail/memory_allocator_impl.hh"
#include "detail/proxy_handler_impl.hh"
#include "detail/redis_impl.hh"
#include "detail/scheduler_impl.hh"
#include "detail/service_layer.hh"
#include "detail/service_logger_impl.hh"
#include "klogger/interface/logger.h"
#include "root/coroutine/coroutine.h"
#include "root/rpc_defines.h"
#include "root/rpc_root.h"
#include "util/lua/lua_helper.hh"
#include "util/object_pool.hh"
#include "util/util_impl.hh"
#include <memory>

kratos::service::ServiceContextImpl::ServiceContextImpl(ServiceBox *box) {
  box_ = box;
}

kratos::service::ServiceContextImpl::~ServiceContextImpl() { box_ = nullptr; }

auto kratos::service::ServiceContextImpl::register_service(
    const std::string &name) -> bool {
  if (name.empty()) {
    return false;
  }
  return box_->register_service(name);
}

auto kratos::service::ServiceContextImpl::unregister_service(
    const std::string &name) -> bool {
  if (name.empty()) {
    return false;
  }
  return box_->unregister_service(name);
}

auto kratos::service::ServiceContextImpl::shutdown() -> void {
  box_->write_log(lang::LangID::LANG_SERVICE_CONTEXT_REQUEST_SHUTDOWN,
                  klogger::Logger::WARNING);
  box_->set_wait_stop_flag();
}

void kratos::service::ServiceContextImpl::sleep_co(std::time_t ms) {
  if (coro_is_main()) {
    // 只能在子协程内调用
    return;
  }
  coro_sleep(ms);
}

auto kratos::service::ServiceContextImpl::get_transport(
    const std::string &service_name, TransportCallback cb)
    -> CallbackAutoDeleterPtr {
  auto cb_id = add_proxy_cb(service_name, cb);
  auto auto_deleter_ptr =
      std::make_unique<CallbackAutoDeleterImpl>(this, cb_id);
  return std::move(auto_deleter_ptr);
}

auto kratos::service::ServiceContextImpl::get_argument() const
    -> const argument::BoxArgument & {
  return box_->get_argument();
}

auto kratos::service::ServiceContextImpl::write_log_line(
    int log_level, const std::string &log_line) -> void {
  if (log_line.empty()) {
    return;
  }
  if (box_) {
    box_->write_log_line(log_level, log_line);
  }
}

auto kratos::service::ServiceContextImpl::get_config() const
    -> kratos::config::BoxConfig & {
  return box_->get_config();
}

auto kratos::service::ServiceContextImpl::try_get_transport(
    const std::string &name) -> std::shared_ptr<rpc::Transport> {
  if (name.empty()) {
    return nullptr;
  }
  return box_->try_get_transport(name);
}

auto kratos::service::ServiceContextImpl::get_transport_sync(
    const std::string &name, std::time_t timeout)
    -> std::shared_ptr<rpc::Transport> {
  if (name.empty()) {
    return nullptr;
  }
  return box_->get_transport_sync(name, timeout);
}

auto kratos::service::ServiceContextImpl::get_transport_co(
    const std::string &service_name, std::time_t timeout)
    -> std::shared_ptr<rpc::Transport> {
  if (service_name.empty()) {
    return nullptr;
  }
  if (coro_is_main()) {
    // 只能在子协程内调用
    return nullptr;
  }
  std::shared_ptr<rpc::Transport> trans;
  std::time_t count = 1;
  while (true) {
    trans = try_get_transport(service_name);
    if (trans) {
      return trans;
    }
    coro_sleep(1);
    if (count < timeout) {
      count += 1;
    } else {
      break;
    }
  }
  return nullptr;
}

auto kratos::service::ServiceContextImpl::verbose(const std::string &log)
    -> void {
  write_log_line(klogger::Logger::VERBOSE, log);
}

auto kratos::service::ServiceContextImpl::info(const std::string &log) -> void {
  write_log_line(klogger::Logger::INFORMATION, log);
}

auto kratos::service::ServiceContextImpl::diagnose(const std::string &log)
    -> void {
  write_log_line(klogger::Logger::DIAGNOSE, log);
}

auto kratos::service::ServiceContextImpl::warn(const std::string &log) -> void {
  write_log_line(klogger::Logger::WARNING, log);
}

auto kratos::service::ServiceContextImpl::except(const std::string &log)
    -> void {
  write_log_line(klogger::Logger::EXCEPTION, log);
}

auto kratos::service::ServiceContextImpl::fail(const std::string &log) -> void {
  write_log_line(klogger::Logger::FAILURE, log);
}

auto kratos::service::ServiceContextImpl::fatal(const std::string &log)
    -> void {
  write_log_line(klogger::Logger::FATAL, log);
}

auto kratos::service::ServiceContextImpl::new_scheduler()
    -> std::unique_ptr<kratos::service::Scheduler> {
  return std::unique_ptr<kratos::service::Scheduler>(
      new kratos::service::SchedulerImpl(box_));
}

auto kratos::service::ServiceContextImpl::new_http()
    -> std::unique_ptr<kratos::http::HttpBase> {
  return std::unique_ptr<kratos::http::HttpBase>(
      new kratos::http::HttpBaseImpl(box_));
}

auto kratos::service::ServiceContextImpl::new_coro_runner()
    -> std::unique_ptr<CoroRunner> {
  return std::unique_ptr<CoroRunner>(new CoroRunnerImpl(this));
}

auto kratos::service::ServiceContextImpl::get_allocator() -> MemoryAllocator & {
  static MemoryAllocatorImpl impl;
  return impl;
}

auto kratos::service::ServiceContextImpl::new_command()
    -> std::unique_ptr<Command> {
  return std::unique_ptr<Command>(new CommandImpl(box_->get_command_manager()));
}

auto kratos::service::ServiceContextImpl::new_redis()
    -> std::unique_ptr<kratos::redis::Redis> {
  return std::unique_ptr<kratos::redis::Redis>(
      (new kratos::redis::RedisImpl(box_)));
}

auto kratos::service::ServiceContextImpl::allocate(std::size_t size) -> void * {
  return reinterpret_cast<void *>(kratos::service::box_malloc(size));
}

auto kratos::service::ServiceContextImpl::deallocate(void *p) -> void {
  return kratos::service::box_free(p);
}

auto kratos::service::ServiceContextImpl::get_statistics() -> ProcStat * {
  return box_->get_proc_stat();
}

auto kratos::service::ServiceContextImpl::get_module_logger(
    const std::string &name) -> std::unique_ptr<ServiceLogger> {
  return std::unique_ptr<ServiceLogger>(new ServiceLoggerImpl(name, box_));
}

auto kratos::service::ServiceContextImpl::get_local_time()
    -> std::unique_ptr<kratos::time::LocalTime> {
  return std::unique_ptr<kratos::time::LocalTime>(
      new kratos::time::LocalTimeImpl());
}

auto kratos::service::ServiceContextImpl::get_proxy_handler()
    -> rpc::ProxyHandler * {
  return box_->get_proxy_handler();
}

auto kratos::service::ServiceContextImpl::get_rpc() -> rpc::Rpc * {
  return box_->get_rpc();
}

auto kratos::service::ServiceContextImpl::get_rpc_statistics()
    -> rpc::StubCallStatistics * {
  return rpc::getStatistic();
}

auto kratos::service::ServiceContextImpl::new_csv_manager() -> CsvManagerPtr {
  return std::unique_ptr<kratos::util::CsvManagerImpl>(
      new kratos::util::CsvManagerImpl(box_));
}

auto kratos::service::ServiceContextImpl::new_box_console(
    const std::string &name) -> BoxConsolePtr {
  return std::unique_ptr<kratos::console::BoxConsoleImpl>(
      new kratos::console::BoxConsoleImpl(name, box_->get_console()));
}

auto kratos::service::ServiceContextImpl::new_lua_service() -> LuaServicePtr {
  return std::unique_ptr<kratos::lua::LuaServiceImpl>(
      new kratos::lua::LuaServiceImpl(box_));
}

auto kratos::service::ServiceContextImpl::new_util() -> UtilPtr {
  return std::unique_ptr<kratos::service::UtilImpl>(
      new kratos::service::UtilImpl());
}

auto kratos::service::ServiceContextImpl::new_co_manager() -> CoManagerPtr {
  return std::make_unique<kratos::service::CoManagerImpl>(this);
}

auto kratos::service::ServiceContextImpl::get_remote_proxy_transport()
    -> std::shared_ptr<rpc::Transport> {
  if (box_->get_argument().get_proxy_host().empty()) {
    return nullptr;
  }
  return box_->try_get_transport("");
}

auto kratos::service::ServiceContextImpl::get_component(
    const std::string &comp_name, const std::string &version)
    -> component::ComponentPtr {
  std::string error;
  auto comp_ptr =
      box_->get_component_factory()->load(comp_name, error, version);
  if (!comp_ptr) {
    box_->write_log_line(klogger::Logger::FAILURE, "[box][component]" + error);
    return nullptr;
  }
  return comp_ptr;
}

auto kratos::service::ServiceContextImpl::add_proxy_cb(
    const std::string &service_name, TransportCallback cb) -> std::uint32_t {
  // 新的回调ID
  auto id = cb_id_++;
  //
  // ServiceLayer事件回调
  //
  ServiceEventCallback layer_cb = [this](const std::string &service_name,
                                         std::uint64_t channel_id,
                                         std::uint32_t cb_id,
                                         ServiceLayerEvent evt) {
    for (auto &[_, info] : cb_map_) {
      if (info.service_name == service_name) {
        if (evt == ServiceLayerEvent::CONNECT) {
          box_->write_log_line(klogger::Logger::INFORMATION,
                               "Service [" + service_name + "] connected");
          auto trans_ptr = box_->get_channel(channel_id);
          if (trans_ptr) {
            // 连接成功触发回调
            info.cb(TransEvent::CONNECT, cb_id,
                    std::dynamic_pointer_cast<rpc::Transport>(trans_ptr));
          } else {
            box_->write_log_line(klogger::Logger::INFORMATION,
                                 "Service [" + service_name +
                                     "] connected, but not found in layer");
          }
        } else if (evt == ServiceLayerEvent::CLOSE) {
          // 连接关闭触发回调
          info.cb(TransEvent::CLOSE, cb_id, nullptr);
          box_->write_log_line(klogger::Logger::INFORMATION,
                               "Service [" + service_name + "] disconnected");
        }
      }
    }
  };
  auto real_name = box_->get_service_layer()->get_real_name(service_name);
  cb_map_[id] = {real_name, cb, layer_cb};
  auto channel_id =
      box_->get_service_layer()->get_channel(real_name, layer_cb, id);
  if (channel_id) {
    auto trans_ptr = box_->get_channel(channel_id);
    if (trans_ptr) {
      box_->write_log_line(klogger::Logger::INFORMATION,
                           "Service [" + real_name + "] found in layer");
      cb(TransEvent::CONNECT, id,
         std::dynamic_pointer_cast<rpc::Transport>(trans_ptr));
    }
  }
  return id;
}

auto kratos::service::ServiceContextImpl::remove_proxy_cb(std::uint32_t cb_id)
    -> void {
  cb_map_.erase(cb_id);
}

auto kratos::service::ServiceContextImpl::schedule(
    kratos::service::CoFunction co_func) -> coroutine::CoroID {
  auto id_ptr = std::make_unique<coroutine::CoroID>(coroutine::INVALID_CORO_ID);
  auto co_id = box_->get_rpc()->spawn(
      [this, co_func](void *ptr) {
        auto id = *reinterpret_cast<coroutine::CoroID *>(ptr);
        try {
          co_func();
        } catch (std::exception &ex) {
          fail(ex.what());
        }
        co_map_.erase(id);
      },
      reinterpret_cast<void *>(id_ptr.get()));
  if (coroutine::INVALID_CORO_ID == co_id) {
    return coroutine::INVALID_CORO_ID;
  }
  *id_ptr = co_id;
  co_map_.emplace(co_id, std::move(id_ptr));
  return co_id;
}

auto kratos::service::ServiceContextImpl::close(coroutine::CoroID co_id)
    -> void {
  auto it = co_map_.find(co_id);
  if (it == co_map_.end()) {
    return;
  }
  box_->get_rpc()->coro_close(co_id);
  co_map_.erase(it);
}

auto kratos::service::ServiceContextImpl::get_box() -> ServiceBox * {
  return box_;
}

kratos::service::CallbackAutoDeleterImpl::CallbackAutoDeleterImpl(
    kratos::service::ServiceContextImpl *ctx_ptr, std::uint32_t cb_id) {
  ctx_ptr_ = ctx_ptr;
  cb_id_ = cb_id;
}

kratos::service::CallbackAutoDeleterImpl::~CallbackAutoDeleterImpl() {
  if (ctx_ptr_) {
    ctx_ptr_->remove_proxy_cb(cb_id_);
  }
}

auto kratos::service::CallbackAutoDeleterImpl::get_cb_id() -> std::uint32_t {
  return cb_id_;
}
